package engine.operators;

import its.opt.structures.EvolutionParams;
import its.util.RandomFactory;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import engine.ObjectiveFunction;
import engine.Operator;
import engine.Population;
import engine.individuals.util.IndividualsComparator;

/**
 * Realizes tournament selection operator
 * 
 * @author rafal.hladyszowski@gmail.com
 * 
 * @param <T>
 */
public class TournamentSelectionOperator<T> implements Operator<T> {

	/** Paramseters of the evolution */
	private final EvolutionParams params;

	private Comparator<T> individualsComparator;
	
	/**
	 * Creates the operator that will take the population and return tournamented
	 * individuals.
	 * 
	 * @param objectiveFunction
	 *          Objective function to score individuals against.
	 */
	public TournamentSelectionOperator(ObjectiveFunction<T> objectiveFunction,
			EvolutionParams params) {
		this.params = params;
		this.individualsComparator = new IndividualsComparator<T>(objectiveFunction);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Population<T> apply(Population<T> population) {

		// initialize some variables

		int N = population.size();

		Population<T> parents = new Population<T>(new ArrayList<T>(N));

		List<T> individuals = population.getIndividuals();
		
		for (int i = 0; i < N; i++) {
			// perform a tournament

			int picked = RandomFactory.getNextInt(N);
			T max = individuals.get(picked);

			for (int j = 1; j < params.tournamentSize; j++) {
				picked = RandomFactory.getNextInt(N);
				T pairedIndividual = individuals.get(picked);
				if (individualsComparator.compare(pairedIndividual, max) > 0) {
					max = pairedIndividual;
				}
			}

			// insert the picked guy into the selected population

			parents.addIndividual(max);
		}
		
		return parents;
	}
}
